Luogu4168 蒲公英 (分块)

题目传送门

题意

长度为n的序列,有m次询问,每次询问求\([l,r]\) 间的众数,如果有多个,输出最小的那个
\(n\le 4\times 10^4,m\le 5\times 10^5,a_i\le10^9\)

分析

题目中要求在线(询问用上次答案加密)。众数不具有“区间可加性”,所以需要分块。
假设分成\(T\)块,每块长度\(L=N/T\)。每次询问\([l,r]\),设\(l\)处在\(p\)块,\(r\)处在\(q\)块,则区间分为三部分。

  • 开头的\([l,L)\)
  • \(第p+1\sim q-1块构成的区间[L,R]\)
  • 结尾的\((R,r]\)
    显然序列在整个询问区间的众数只可能出现在\([L,R]\)中的众数,或者头尾出现的数字中。
    可以预处理所有分块区间的众数\(d[l][r]\)表示\(l\)块到\(r\)块中的众数。复杂度\(O(TN)\)
    对于头尾的部分,暴力枚举每个数字,通过预先存在每个数字出现的位置,这样就可以二分查询该数字\([l,r]\)出现的次数,然后选取最大的即可。复杂度\(NM/T*log(N)\)
    所以总复杂度为\(O(TN+NM/T*log(N))\),又N和M几乎同级,可得\(T = \sqrt{Mlog(N)}\)时较优。
    更多细节请看代码

思路&步骤

  1. 存下每个数字,离散化,处理每个数字出现的位置,储存在STL::vector里面
  2. 分块,预处理整区间的众数
  3. 查询,先取整个区间的答案,然后两头扫
#include <bits/stdc++.h>
using namespace std;
const int N = 40010;
const int M = 500010;
/*
be[i] 表示 i 在第几块
id[i] 表示a[i]在离散化数组中的位置
blc为块大小
cnt[i] 预处理时需要的数组,表示 i 的个数
d[i][j]表示 i块与 j块之间的众数
v[i] 表示 数字 i 出现的位置
g为离散化vector
*/
int be[N],id[N],a[N],n,m;
int blc,cnt[N],d[1200][1200];
vector<int> v[N],g;

int getID(int x){
    return lower_bound(g.begin(),g.end(),x) - g.begin();
}
//预处理整块区间众数
void build(int x){
    int res = -1;
    for(int i=0;i<g.size();i++)cnt[i] = 0;
    for(int i=(x-1)*blc+1;i<=n;i++){
        cnt[id[i]] ++;
        if(res == -1 || cnt[id[i]] > cnt[res] || (cnt[id[i]] == cnt[res] && id[i] < res)){
            res = id[i];
        }
        d[x][be[i]] = res;
    }
}
//询问[l,r]间x的个数
int ask(int l,int r,int x){
    return upper_bound(v[x].begin(),v[x].end(),r) - lower_bound(v[x].begin(),v[x].end(),l);
}
int query(int l,int r){
	//mxcnt为当前众数的个数,res为答案
    int mxcnt = 0,res = -1;
    if(be[r] - be[l] > 1){
        res = d[be[l]+1][be[r]-1];
        mxcnt = ask(l,r,res);
    }
    if(be[r] == be[l]){
        for(int i=l;i<=r;i++){
            int cnt = ask(l,r,id[i]);
            if(res == -1 || mxcnt < cnt || (mxcnt == cnt && id[i] < res)){
                res = id[i];
                mxcnt = cnt;
            }
        }
    }
    else{
        for(int i=l;i<=be[l] * blc;i++){
            int cnt = ask(l,r,id[i]);
            if(res == -1 || mxcnt < cnt || (mxcnt == cnt && id[i] < res)){
                res = id[i];
                mxcnt = cnt;
            }
        }
        for(int i=(be[r]-1) * blc;i<=r;i++){
            int cnt = ask(l,r,id[i]);
            if(res == -1 || mxcnt < cnt || (mxcnt == cnt && id[i] < res)){
                res = id[i];
                mxcnt = cnt;
            }
        }
    }
    return res;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        g.push_back(a[i]);
    }
    sort(g.begin(),g.end());g.erase(unique(g.begin(),g.end()),g.end());
    for(int i=1;i<=n;i++){
        id[i] = getID(a[i]);
        v[id[i]].push_back(i);
    }
    blc = max(1,(int)(n/sqrt(m * log2(n))));
    for(int i=1;i<=n;i++){
        be[i] = (i-1) / blc + 1;
    }
    for(int i=1;i<=be[n];i++)
        build(i);
    int res = 0;
    while(m--){
        int l,r;
        scanf("%d%d",&l,&r);
        l = (l + res - 1) % n + 1;
        r = (r + res - 1) % n + 1;
        if(l > r)swap(l,r);
        res = g[query(l,r)];
        printf("%d\n",res);
    }
    return 0;
}
posted @ 2019-08-06 16:27  kpole  阅读(172)  评论(0编辑  收藏  举报